security(qa): sanitize assistant markdown rendering in QA panel#241
Conversation
Issue Open-Less#221 requires closing the QA markdown XSS surface without broad CSP or architecture changes. This switches assistant and streaming markdown to one shared safe renderer that escapes raw HTML and rejects non-http(s)/mailto/tel links. Constraint: Keep markdown readability while preventing executable HTML in QA WebView Rejected: Add DOMPurify dependency | avoid new dependency for minimal fix Confidence: medium Scope-risk: narrow Directive: Keep streaming and final assistant rendering on the same sanitizer path Tested: npm run build (openless-all/app) Not-tested: Manual malicious payload interaction in packaged desktop app
Two parallel reviews flagged a high-risk fallback path and a link-encoding regression in the new QA sanitizer. This patch keeps the minimal Open-Less#221 approach while ensuring failure fallback stays inert and safe links preserve query strings correctly. Constraint: Preserve minimal change scope for issue Open-Less#221 Rejected: Expand into CSP/capability hardening in same patch | out of issue scope and higher blast radius Confidence: high Scope-risk: narrow Directive: Never return raw model output into dangerouslySetInnerHTML fallback paths Tested: npm run build (openless-all/app) Not-tested: Manual desktop exploit reproduction on Windows/macOS
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7edd0cde9e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const escaped = escapeHtml(markdown); | ||
| return marked.parse(escaped, { | ||
| async: false, | ||
| gfm: true, |
There was a problem hiding this comment.
Preserve hard line breaks in QA markdown renderer
This refactor removed the previous marked.setOptions({ breaks: true }) behavior and the new marked.parse call does not set breaks, so single newlines in assistant output are no longer rendered as line breaks. In QA responses (especially streamed text and lightly formatted lists), this collapses content into denser paragraphs and is a user-visible regression from prior behavior in QaPanel.tsx.
Useful? React with 👍 / 👎.
Code review flagged a UX regression after sanitizer refactor: assistant single newlines stopped rendering as hard breaks. Re-enable marked breaks mode in the shared QA renderer to preserve previous panel readability for streamed and lightly formatted answers. Constraint: Keep sanitizer fix while matching prior QA markdown display behavior Rejected: Add newline post-processing in QaPanel | duplicates renderer concern and increases drift risk Confidence: high Scope-risk: narrow Directive: QA markdown display flags must be centralized in qaMarkdown.ts to keep streaming/final parity Tested: npm run build (openless-all/app) Not-tested: Manual visual QA panel check on desktop runtime
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
PR Reviewer Guide 🔍(Review updated until commit 806fa26)Here are some key observations to aid the review process:
|
|
Persistent review updated to latest commit d397f92 |
Review feedback found that pre-escaping the entire markdown payload caused double-encoding in inline/fenced code, making HTML/JSON/XML examples unreadable. Switch to raw markdown parsing with renderer-level HTML token escaping so code formatting stays readable while raw HTML remains inert. Constraint: Maintain issue Open-Less#221 security boundary without broad sanitizer rewrite Rejected: Keep full-string pre-escape | breaks code snippet readability Confidence: high Scope-risk: narrow Directive: Escape only raw HTML tokens, not the whole markdown source Tested: npm run build (openless-all/app) Not-tested: Manual QA panel rendering across all markdown edge cases
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
Persistent review updated to latest commit 806fa26 |
User description
Summary
Validation
Closes #221
PR Type
Bug fix, Tests
Description
Centralize markdown sanitization in a shared renderer
Escape raw HTML and restrict link protocols
Unify streaming and final answer rendering paths
Harden error fallback to prevent unsafe injection
Diagram Walkthrough
File Walkthrough
qaMarkdown.ts
Add safe QA markdown renderer with XSS guardsopenless-all/app/src/lib/qaMarkdown.ts
QaPanel.tsx
Adopt safe markdown renderer in QaPanelopenless-all/app/src/pages/QaPanel.tsx
qaMarkdown.test.ts
Add regression tests for markdown sanitizationopenless-all/app/src/lib/qaMarkdown.test.ts